package link.jfire.socket.socketserver.storage.ringarray;

import java.util.concurrent.locks.LockSupport;
import link.jfire.baseutil.reflect.ReflectUtil;
import link.jfire.baseutil.verify.Verify;
import link.jfire.socket.socketserver.exception.ServerStopException;
import link.jfire.socket.socketserver.handler.MessageAction;
import link.jfire.socket.socketserver.storage.CpuCachePadingValue;
import link.jfire.socket.socketserver.storage.Entry;
import link.jfire.socket.socketserver.storage.waitstrategy.WaitStrategy;
import sun.misc.Unsafe;

@SuppressWarnings("restriction")
public class ComplexMultRingArray implements RingArray
{
    private Entry[]               entries;
    private int[]                 availableFlags;
    // size是2的几次方幂，右移该数字相当于除操作
    private int                   flagShift;
    private int                   size;
    private int                   sizeMask;
    // 代表下一个可以增加的位置
    private CpuCachePadingValue   add    = new CpuCachePadingValue();
    private volatile long         wrapPoint;
    private final WaitStrategy    waitStrategy;
    private volatile int          state  = 0;
    private final MessageAction[] actions;
    private static Unsafe         unsafe = ReflectUtil.getUnsafe();
    private static int            STOPED = 1;
    private static long           base;
    private static int            shift;
    private static int            intShift;
    static
    {
        base = unsafe.arrayBaseOffset(Entry[].class);
        int scale = unsafe.arrayIndexScale(Entry[].class);
        if (scale == 8)
        {
            shift = 3;
        }
        else if (scale == 4)
        {
            shift = 2;
        }
        else
        {
            throw new RuntimeException("不认识");
        }
        scale = Unsafe.ARRAY_INT_INDEX_SCALE;
        if (scale == 8)
        {
            intShift = 3;
        }
        else if (scale == 4)
        {
            intShift = 2;
        }
        else
        {
            throw new RuntimeException("不认识");
        }
    }
    
    public ComplexMultRingArray(int size, WaitStrategy waitStrategy, MessageAction[] actions)
    {
        Verify.True(size > 1, "数组的大小必须大于1");
        Verify.True(Integer.bitCount(size) == 1, "数组的大小必须是2的次方幂");
        entries = new Entry[size];
        availableFlags = new int[size];
        this.actions = actions;
        for (int i = 0; i < size; i++)
        {
            entries[i] = new Entry();
            availableFlags[i] = -1;
        }
        this.size = size;
        sizeMask = size - 1;
        this.waitStrategy = waitStrategy;
        wrapPoint = size - 1;
        flagShift = Integer.numberOfTrailingZeros(size);
    }
    
    public long next()
    {
        long next = add.next();
        do
        {
            if (next >= wrapPoint)
            {
                LockSupport.parkNanos(1);
                wrapPoint = getMin() + size;
            }
            else
            {
                return next;
            }
        } while (true);
    }
    
    public Entry entryAt(long cursor)
    {
        return (Entry) unsafe.getObjectVolatile(entries, base + ((cursor & sizeMask) << shift));
    }
    
    public void publish(long cursor)
    {
        unsafe.putIntVolatile(availableFlags, Unsafe.ARRAY_INT_BASE_OFFSET + ((cursor & sizeMask) << intShift), (int) (cursor >>> flagShift));
        waitStrategy.signallBlockwaiting();
    }
    
    public boolean isAvailable(long cursor)
    {
        if ((int) (cursor >>> flagShift) == unsafe.getIntVolatile(availableFlags, Unsafe.ARRAY_INT_BASE_OFFSET + ((cursor & sizeMask) << intShift)))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    public long cursor()
    {
        return add.getPoint();
    }
    
    public void waitFor(long cursor) throws ServerStopException
    {
        waitStrategy.waitFor(cursor, this);
    }
    
    private long getMin()
    {
        long min = Long.MAX_VALUE;
        for (MessageAction each : actions)
        {
            if (min > each.cursor())
            {
                min = each.cursor();
            }
        }
        return min;
    }
    
    public void stop()
    {
        state = STOPED;
        waitStrategy.signallBlockwaiting();
    }
    
    public boolean stoped()
    {
        return state == STOPED;
    }
}
